import shutil
import os
import pymysql
import yaml
from order import Item, Order,BatchSize
import math
from itertools import groupby
from typing import List, Tuple
import numpy as np
from hrpacker import Item, BinManager
from copy import deepcopy
from load_files import load_pieces_from_flask
import re
import db as crud
def get_batch_num(itemsize,batch_size):
    if itemsize<batch_size:
        batch_num=1
    else:
        max_batch_num = math.floor(itemsize/ batch_size)
        min_batch_num=math.ceil(itemsize/ batch_size)
        max1=math.fabs((batch_size-(itemsize/max_batch_num)))
        min1=math.fabs((batch_size-(itemsize/min_batch_num)))
        if max1<min1:
            batch_num=max_batch_num
        else:
            batch_num =min_batch_num
    return batch_num


def connect_db(host,user,password,database):
    config = dict(host=host, user=user, password=password, database=database,
                  cursorclass=pymysql.cursors.DictCursor)

    conn = pymysql.Connect(**config)
    # 自动确认commit True
    conn.autocommit(1)
    # 设置光标
    cursor = conn.cursor()
    return  conn,cursor



def to_db(items, index, ratio, order_len, square_workpieces, total_square):
    data = []
    for item in items:
        year=(item.details["cutting_time"].split('-')[0])[-2:]
        month_day=(item.details["cutting_time"].split('-')[1]).zfill(2)+ (item.details["cutting_time"].split('-')[2]).zfill(2)
        batchNumber = item.details["code"] + year + month_day + str(index).zfill(2) + "-" + str(order_len) + "-" + \
                      item.details["optimization_type"] + "-" + '1'
        data.append(crud.OrderPooled(
            # TODO 转换参数
        ))
    return crud.save_order_pooled(data)

def to_csv(items, name):
    """
    写出单个批次数据
    @param items:
    @param name:
    @return:
    """
    template_dir = './csv/template'
    shutil.copyfile(os.path.join(template_dir, 'template.csv'), name)
    with open(name, 'a', encoding='gbk') as f:
        for item in items:
            f.write(item.details)

def pack(items1: List[Item], cut_rim, print_info: bool = True, name=None) -> Tuple[BinManager, float]:
    items = deepcopy(items1)
    config = crud.get_config()
    rotate = config.rotate
    length = config.length
    width = config.width
    M = BinManager(length - cut_rim * 2, width - cut_rim * 2, pack_algo='maximal_rectangle', heuristic='best_area',
                   rectangle_merge=False,
                      rotation=rotate)
    M.add_items(*items)
    M.execute()
    ratio = -1
    if print_info:
        prod = 0
        for bin in M.bins:
            prod += sum([item.width * item.height for item in bin.items])
            ratio = prod * 100 / (length * width * len(M.bins))
        # print(f"{name},优化{len(items)}片，利用率：{ratio}")
    print(prod)
    print(length * width * len(M.bins))
    return M, ratio,prod,length * width * len(M.bins)

def group_items_by_oid(items: List[Item]):
    '''
    将items根据订单号进行组合，相同订单号的结合到一起
    :param items: 我们需要的ITEM（list）
    :return: 字典（key：订单号  value ：一个list里面存储的是这个订单号所对应的item）
    '''
    return dict([(key, list(group)) for key, group in groupby(items, key=lambda item: item.oid)])  # lambda表达式 正则表达式


def load_pieces_by_oids(items: List[Item], oids: List[str]) -> List[Item]:
    ids = set(oids)
    return [item for item in items if item.oid in ids]

def sort_orders(orders):
    order_size = [order.__len__() for order in orders]
    order_size = np.array(order_size)
    order_size_indexs = np.argsort(order_size)

    # argsort() 函数返回的是数组值从小到大的索引值。
    return order_size_indexs

def sort_order_form_batch(batches, each_order_in_batch,cut_rim):

    orders_sorted=[]
    for batch in batches:
        now_M, now_ratio ,square_workpieces,total_square= pack(batch, cut_rim=cut_rim, print_info=True)
        print(now_ratio)

        orders_sorted.append(BatchSize(batch,now_ratio))
    orders_sorted1 = sorted(orders_sorted,key=lambda x:x.size,reverse=True)
    return orders_sorted1

def order_init(orders,batch_num,cut_rim):
    order_size_indexs = sort_orders(orders)
    each_order_in_batch = []
    batches = []
    oid_used = set()
    # set() 函数创建一个无序不重复元素集，可进行关系测试，删除重复数据，还可以计算交集、差集、并集等。
    for batch in range(batch_num):
        now_batch = []
        this_order_in_batch = []
        for i, index in enumerate(order_size_indexs):
            # enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列，同时列出数据和数据下标
            if i % batch_num == batch and len(now_batch) + len(orders[index].items) >= 300:
                break
            elif i % batch_num == batch and index not in oid_used:
                now_batch.extend(orders[index].items)
                oid_used.add(index)
                this_order_in_batch.append(index)
            else:
                continue
        batches.append(now_batch)
        each_order_in_batch.append(this_order_in_batch)
    orders_unused = set(order_size_indexs) - oid_used
    print('未使用的order_id为:', orders_unused)
    # 当还有orders时,对所有order进行分配直到所有的都放进去

    while len(orders_unused) != 0:
        print('未使用的order_id长度为:', len(orders_unused))
        for batch_id in batches:
            max_ratio = 0
            final_order_id = -1
            if len(orders_unused) == 0:
                break
            for order_id in orders_unused:
                temp = deepcopy(batch_id)
                temp.extend(orders[order_id].items)
                now_M, now_ratio ,square_workpieces,total_square= pack(temp, cut_rim=cut_rim, print_info=True)
                if now_ratio > max_ratio:
                    max_ratio = now_ratio
                    final_order_id = order_id
            batch_id.extend(orders[final_order_id].items)
            orders_unused.remove(final_order_id)
            each_order_in_batch[batches.index(batch_id)].append(final_order_id)

    t = 8
    print('初始化batches各个item数以及利用率为：')
    cnt_boards = 0
    summary_ratio = 0
    for batch in batches:
        now_M, now_ratio ,square_workpieces,total_square= pack(batch, cut_rim=cut_rim, print_info=True)
        cnt_boards += len(now_M.bins)
        summary_ratio += len(now_M.bins) * now_ratio
        print(len(now_M.items))
        print(now_ratio)
        # to_csv(batch_id, save_dir + '//' + str(t) + '.csv')
        t += 1
    final_ratio=format((summary_ratio / cnt_boards),'.3f')

    # print(summary_ratio)
    # print(cnt_boards)
    print('初始化后总利用率为{0}，一共消耗{1}个板材'.format(final_ratio,cnt_boards))
    return batches,each_order_in_batch,cnt_boards

def search_batches_GP(orders, batch_num,best_cnt_boards,cut_rim,depart_num,optimization_type,datatime):
    batches,each_order_in_batch,cnt_boards_temp=order_init(orders, batch_num, cut_rim)
    final_batches = []
    max_id = -1
    min_id = -1
    num = 0
    batch_num_temp=batch_num
    best_cnt_boards1=best_cnt_boards
    if batch_num>2:
        best_cnt_boards1+=1

    while num < 100:
        print('这是第{0}次循环，循环之前的batch消耗的板材数量为{1}'.format(num + 1,cnt_boards_temp))
        take_order_time = 0
        if cnt_boards_temp==best_cnt_boards1:
            print('优化效果达到预期，退出')
            break
        oders_sorted = sort_order_form_batch(batches, each_order_in_batch, cut_rim=cut_rim)
        minorders=each_order_in_batch[(batches.index(oders_sorted[batch_num_temp-1].batch)) ]
        min_id=each_order_in_batch.index(minorders)
        final_order_id = -1
        f = open(r'./config/config.yaml', 'r', encoding='utf-8')
        result = f.read()
        # print(result)
        # 转换成字典读出来
        # a = yaml.load(result)
        a = yaml.load(result, Loader=yaml.FullLoader)
        utilizationrate = a['utilizationrate']
        for each_order in oders_sorted:
            if each_order_in_batch[(batches.index(each_order.batch)) ]==minorders :
                continue
            maxorders=each_order_in_batch[(batches.index(each_order.batch)) ]
            max_id=each_order_in_batch.index(maxorders)
            print('此次要在第{0}个batch中取order放入第{1}个order,此时的batch_num_temp为{2}'.format(max_id,min_id,batch_num_temp-1))
            for i in maxorders:
                print('take_order_time={0},utilizationrate*len(orders)={1}'.format(take_order_time,utilizationrate*len(orders)))
                if take_order_time == utilizationrate*len(orders):
                    print('拿取order次数过多，退出循环')
                    break
                take_order_time += 1
                cnt_boards = 0
                summary_ratio = 0
                batches_temp = deepcopy(batches)
                maxorders1 = deepcopy(maxorders)
                batches_temp[min_id].extend(orders[i].items)
                maxorders1.remove(i)
                batch0 = []
                for or1 in maxorders1:
                    batch0.extend(orders[or1].items)
                batches_temp[max_id] = batch0
                for batch_temp in batches_temp:
                    now_M, now_ratio ,square_workpieces,total_square= pack(batch_temp, cut_rim=cut_rim, name='测试', print_info=True)
                    cnt_boards += len(now_M.bins)
                    summary_ratio += len(now_M.bins) * now_ratio
                summary_ratio = summary_ratio / cnt_boards
                # print(summary_ratio1)
                # print(cnt_boards)
                # print(type(final_ratio))
                summary_ratio=format(summary_ratio,'.3f')
                print('这次测试取第{0}个order的综合利用率为{1},final利用率为{2}'.format(i, summary_ratio, final_ratio))
                if summary_ratio > final_ratio:
                    final_ratio = summary_ratio
                    final_order_id = i  # final_order_id是订单号
                    cnt_boards_temp=cnt_boards
                    print('此时summary_ratio大于final_ratio')
                    print('此次循环选取第{0}个订单,该次循环退出'.format(final_order_id))
                    take_order_time=0
                    if batch_num!=batch_num_temp:
                        batch_num_temp=batch_num
                    break
            # print('此次循环选取第{0}个订单'.format(final_order_id))
                # 如果在利用率最大的利用率里找不到合适的order
            if final_order_id == -1:
                print('在第{0}个batch中没有合适的order，切换下一个batch'.format(max_id))
                num+=1
                continue

            if final_order_id != -1:
                break
        if batch_num_temp-1==0:
            break
        if final_order_id == -1:
            print('选不出可以使综合利用率提高的batch,改成利用率次之小的batch')
            batch_num_temp-=1
            continue
        maxorders.remove(final_order_id)
        each_order_in_batch[max_id] = maxorders
        batch1 = []
        for order in maxorders:
            batch1.extend(orders[order].items)
        batches[max_id] = batch1
        # rrr=each_order_in_batch.index(minorders)
        minorders.append(final_order_id)
        each_order_in_batch[min_id] = minorders
        batches[min_id].extend(orders[final_order_id].items)
    for batch in batches:
        final_batches.append(batch)
    batches = final_batches
    cnt_boards = 0

    count = crud.get_order_pooled_count(depart_num,optimization_type,datatime)

    print('count=={0}'.format(count))

    # print(batches[2])
    summary_ratio = 0
    all_M = []
    for batch_id in batches:  # 计算板材数量并且保存batch至tmp文件中
        count += 1
        now_M, now_ratio ,square_workpieces,total_square= pack(batch_id, name='最后的batch优化', cut_rim=cut_rim, print_info=True)
        cnt_boards += len(now_M.bins)
        summary_ratio += len(now_M.bins) * now_ratio
        print('划分完的batch_size,板材数量,每个batch的ratio分别是:', len(batch_id), len(now_M.bins), now_ratio)

        # to_csv(batch_id, save_dir + '//' + str(count) + '.csv')
        now_ratio1=format(now_ratio,'.3f')
        oids_names = sorted(list(set([item.oid for item in batch_id])))
        to_db(batch_id,count,now_ratio1,len(oids_names),square_workpieces,total_square)


        # # board_save_dir = './partterns/' + '//' + str(count)
        # if not os.path.exists(board_save_dir):  # 判断是否存在文件夹如果不存在则创建为文件夹
        #     os.makedirs(board_save_dir)
        # save_patterns(now_M, board_save_dir)

    summary_ratio = summary_ratio / cnt_boards
    # save_patterns(all_M, './partterns/')
    return None, cnt_boards, summary_ratio

def calc_opt_ratio(searchlists) :
    config = crud.get_config()
    cut_rim = config.cut_rim
    batch_size=config.batch_size
    items1, all_mat_names,dic,depart_num,optimization_type,datatime= load_pieces_from_flask(searchlists)
    # print(dic)
    for mat_names in all_mat_names:
        print('----------------------------{0}开始优化---------------------------'.format(mat_names))
        items=dic[mat_names]
        orders = []
        oids_names = sorted(list(set([item.oid for item in items])))
        #找出所有的item的订单号，订单号不重复
        # sorted() 函数对所有可迭代的对象进行排序操作
        # set() 函数创建一个无序不重复元素集
        # oids_names订单名字组成的list
        grps = group_items_by_oid(items)
        # 订单名字（里面包含每一个该订单的所有ITem详情）
        real_items=[]
        for name in oids_names:
            order_items = grps[name]
            orders.append(Order(oids=name, num=len(order_items), items=order_items))
            real_items.extend(order_items)
        items=real_items
        print('{0}   一共有{1}个order,一共包含{2}个piece'.format(mat_names,len(orders), len(items)))
        now_M, now_ratio ,square_workpieces,total_square= pack(items, name='所有订单一个批次的利用率优化', cut_rim=cut_rim, print_info=True)
        best_cnt_boards=len(now_M.bins)
        print('{0}   所有的做成一个批次消耗的板材数为{1}利用率为{2}'.format(mat_names,len(now_M.bins), now_ratio))

        batch_num=get_batch_num(len(items),batch_size)

        batch_size = math.floor(len(items) / batch_num)
        print('{0}   建议分成{1}个批次，每个批次平均{2}个piece'.format(mat_names,batch_num, batch_size))
        search_batches_GP(orders, batch_num, best_cnt_boards,cut_rim,depart_num,optimization_type,datatime)
        print('----------------------------{0}优化结束---------------------------'.format(mat_names))
    # return batch_size, ratio_max, cnt_boards


if __name__ == '__main__':
    calc_opt_ratio()
